import 'dart:async';
import 'dart:collection';
import 'dart:ui' as ui;
import 'package:flutter/animation.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'dart:math';

///知识图谱球
class KnowledgeDomainLearingWidget extends StatefulWidget {
  @override
  _KnowledgeDomainLearingWidgetState createState() =>
      _KnowledgeDomainLearingWidgetState();
}

class _KnowledgeDomainLearingWidgetState
    extends State<KnowledgeDomainLearingWidget>
    with SingleTickerProviderStateMixin {
  //知识球的位置集合
  late Map<int, _CircleOffset> domainBall = HashMap();

  var _existPoint = false;
  var _operatePointIndex = -1;

  double radius = 30;

  @override
  void initState() {
    super.initState();

    initCirclePoint();
  }

  void initCirclePoint() {
    domainBall.clear();
    int count = 20;
    //周长等于2Πr，所有小圆围成一个大圆的话，近似就是每个小圆的直径2r * 个数count
    double bigCircleRadius = 2 * (radius + 0) * count / 2 / pi;
    print("大圆半径：$bigCircleRadius");
    for (int i = 0; i < count+1; i++) {
      double du = 2 * pi / count * i;
      double duY = sin(du) * bigCircleRadius;
      double duX = cos(du) * bigCircleRadius;

      int lineTo = 0;
      //随机线
      // int lineTo = Random().nextInt(count-1);
      //初始圆心点为400，400，然后计算第几个小圆，占据的弧度角，计算出，x，y差值，重新确定圆心
      if (i == 0) {
        domainBall[i] = _CircleOffset(
            x: 400 + bigCircleRadius - duX,
            y: 400 + duY,
            color: Color(0xFFE02222),
            lineTo: 1);
      }else {
        domainBall[i] = _CircleOffset(
            x: 400 + bigCircleRadius - duX,
            y: 400 + duY,
            color: Color(0xFFE02222),lineTo: 0);
      }
    }
  }

  void _movePoint(PointerMoveEvent details, Size? widgetSize) {
    double widgetWidth = widgetSize?.width != null ? widgetSize!.width : 0;
    double widgetHeight = widgetSize?.height != null ? widgetSize!.height : 0;
    if (details.localPosition.dx - radius < 0 ||
        details.localPosition.dy - radius < 0 ||
        details.localPosition.dx + radius > widgetWidth ||
        details.localPosition.dy + radius > widgetHeight) {
      // print("details.size----越界 ${widgetSize?.width}");
      return;
    }

    if (_operatePointIndex != -1 && _existPoint) {
      var moveX = details.localPosition.dx;
      var moveY = details.localPosition.dy;
      var canMove = true;

      for (var key in domainBall.keys) {
        if (key != _operatePointIndex) {
          var otherCircle = domainBall[key];
          var disX = (otherCircle!.x - moveX).abs();
          var disY = (otherCircle.y - moveY).abs();
          var dis = sqrt(disX * disX + disY * disY);
          if (dis < radius * 2) {
            // print("距离：$dis");
            canMove = false;
            break;
          }
        }
      }

      canMove = true;
      if (canMove) {
        // print("移动谁：$_operatePointIndex-----移动点X：$moveX---y:$moveY");
        domainBall[_operatePointIndex] = _CircleOffset(
            x: moveX,
            y: moveY,
            color: domainBall[_operatePointIndex]?.color,
            lineTo: domainBall[_operatePointIndex]?.lineTo);
        setState(() {});
      }
    }
  }

  void _addPoint(PointerUpEvent details) {
    // if (_operatePointIndex != -1 && _existPoint) {
    //   domainBall[_operatePointIndex] =
    //       _CircleOffset(x:details.localPosition.dx, y:details.localPosition.dy);
    // }
    // _existPoint = false;
  }

  bool _existDownPoint(PointerDownEvent details) {
    var exist = false;
    var downX = details.localPosition.dx;
    var downY = details.localPosition.dy;

    for (var key in domainBall.keys) {
      var otherCircle = domainBall[key];
      var disX = (otherCircle!.x - downX).abs();
      var disY = (otherCircle.y - downY).abs();
      var dis = sqrt(disX * disX + disY * disY);
      if (dis < radius) {
        exist = true;
        _operatePointIndex = key;
        break;
      }
    }

    return exist;
  }

  @override
  Widget build(BuildContext context) {
    return Material(
        child: Center(
      child: Container(
          child: Listener(
        onPointerDown: (details) {
          _existPoint = _existDownPoint(details);
          print(
              "onPointerDown _existDownPoint:  ${_existPoint}---_operatePointIndex:$_operatePointIndex");
        },
        onPointerMove: (details) {
          if (_existPoint) {
            // print("object:${details.localPosition}----${details.distance}");

            _movePoint(details, context.size);
          }
        },
        onPointerUp: (details) {
          _existPoint = false;
          _addPoint(details);
        },
        child: Stack(
          children: [
            Container(
              width: double.infinity,
              height: double.infinity,
              color: Color(0xffffffff),
              child: RepaintBoundary(
                //隔离重绘控件，singlescrollview 滑动发生重绘，导致画笔paint响应
                child: CustomPaint(
                    painter: _CirclePainter(
                        radius: radius,
                        operatePointIndex:_operatePointIndex,
                        domainBall: domainBall,
                        painterColor: Color(0xFF0C74F3))),
              ),
            ),
          ],
        ),
      )),
    ));
  }

  @override
  void dispose() {
    super.dispose();
  }
}

class _CircleOffset {
  _CircleOffset({required this.x, required this.y, this.color, this.lineTo});

  Color? color = Color(0xFFFA0601);
  int? lineTo = -1;
  double x;

  double y;
}

class _CirclePainter extends CustomPainter {
  Color painterColor;
  late Map<int, _CircleOffset> domainBall = HashMap();
  double radius;
  int operatePointIndex;

  _CirclePainter(
      {required this.domainBall,
      required this.painterColor,
      required this.operatePointIndex,
      required this.radius});

  @override
  void paint(Canvas canvas, Size size) {
    Paint _painter = Paint()
      ..color = painterColor
      ..style = PaintingStyle.fill
      ..isAntiAlias = true;

    Paint _painterLine = Paint()
      ..color = Color(0xFFF50000)
      ..style = PaintingStyle.stroke
      ..isAntiAlias = true;

    ///大圆
    Paint _bigPainterLine = Paint()
      ..color = Color(0xFFFD4C00)
      ..style = PaintingStyle.stroke
      ..isAntiAlias = true;

    canvas.drawCircle(
        Offset(400, 400), 190.9, _bigPainterLine);

    // if(operatePointIndex!=-1 && domainBall[operatePointIndex] !=null){
    //   var circleOffset = domainBall[operatePointIndex]!;
    //   // print("画圆：${circleOffset.x}");
    //   _painter.color = Color.fromRGBO(Random().nextInt(150), Random().nextInt(150), Random().nextInt(150), 1);
    //
    //   canvas.drawCircle(
    //       Offset(circleOffset.x, circleOffset.y), radius, _painter);
    // }else {
    //   for (var key in domainBall.keys) {
    //     var circleOffset = domainBall[key]!;
    //     // print("画圆：${circleOffset.x}");
    //     _painter.color = Color.fromRGBO(
    //         Random().nextInt(150), Random().nextInt(150), Random().nextInt(150),
    //         1);
    //
    //     canvas.drawCircle(
    //         Offset(circleOffset.x, circleOffset.y), radius, _painter);
    //   }
    // }
    for (var key in domainBall.keys) {
      var circleOffset = domainBall[key]!;
      // print("画圆：${circleOffset.x}");
      _painter.color = Color.fromRGBO(
          Random().nextInt(150), Random().nextInt(150), Random().nextInt(150),
          1);

      canvas.drawCircle(
          Offset(circleOffset.x, circleOffset.y), radius, _painter);
    }

    for (var key in domainBall.keys) {
      var circleOffset = domainBall[key]!;


      if (circleOffset.lineTo != -1 &&
          circleOffset.lineTo != null &&
          domainBall[circleOffset.lineTo!] != null && circleOffset.lineTo != key) {
        var targetCircle = domainBall[circleOffset.lineTo!]!;

        var p00 = Offset(
          circleOffset.x,
          circleOffset.y,
        );
        var p03 = Offset(
          targetCircle.x,
          targetCircle.y,
        );


        double controlX = 0;
        double controlY = 0;

        double distanceX = 0;
        double distanceY = 0;

        // if (p00.dx > p03.dx) {
        //   controlX = p03.dx + (p03.dx - p00.dx).abs() / 2;
        //   if (p00.dy > p03.dy) {
        //     controlY = p03.dy + (p03.dy - p00.dy).abs() / 2;
        //   } else {
        //     controlY = p00.dy + (p03.dy - p00.dy).abs() / 2;
        //   }
        // } else {
        //   controlX = p00.dx + (p03.dx - p00.dx).abs() / 2;
        //   if (p00.dy > p03.dy) {
        //     controlY = p03.dy + (p03.dy - p00.dy).abs() / 2;
        //   } else {
        //     controlY = p00.dy + (p03.dy - p00.dy).abs() / 2;
        //   }
        // }


        //取两圆x，y长度
        distanceY=(p03.dy - p00.dy).abs();
        distanceX = (p03.dx - p00.dx).abs();

        double twoPointDistance = sqrt(distanceY * distanceY + distanceX * distanceX);
        //邻边比斜边得出角度cos，   //计算出比例点
        var cosAngle =distanceY/twoPointDistance;
        var sinAngle =distanceX/twoPointDistance;
        //取目标圆上点的截取长度
        double linkLineY = (twoPointDistance-radius)*cosAngle;
        double linkLineX = (twoPointDistance-radius)*sinAngle;

        //取目标圆上点的废弃的长度
        double noLinkLineY = distanceY-linkLineY;
        double noLinkLineX = distanceX-linkLineX;

        // print("object:drawlinkLineX:$linkLineX---linkLineY:$linkLineY");
        // print("object:drawnolinkLineX:$noLinkLineX---noLinkLineY:$noLinkLineY");
        // print("object:drawdistanceX:$distanceX---distanceY:$distanceY");

        //目标点
        double endX = 0;
        double endY = 0;



        if (p00.dx > p03.dx) {
          if (p00.dy > p03.dy) {
            endY =  p03.dy + (noLinkLineY).abs();
            controlY = p03.dy + linkLineY/ 2;
          } else {
            endY =  p03.dy - (noLinkLineY).abs();
            controlY = p03.dy - linkLineY/ 2;
          }
          endX = p03.dx+(noLinkLineX).abs();
          controlX = endX + linkLineX / 2;
        } else {
          if (p00.dy > p03.dy) {
            endY =  p03.dy + (noLinkLineY).abs();
            controlY = p03.dy + linkLineY/ 2;
          } else {
            endY =  p03.dy - (noLinkLineY).abs();
            controlY = p03.dy - linkLineY/ 2;
          }
          endX = p03.dx-(noLinkLineX).abs();
          controlX = endX - linkLineX / 2;
        }


        double  centerX = (p00.dx+endX)/2;
        double centerY = (p00.dy+endY)/2;
        print("object:drawLinecontrolX:$controlX---controlY:$controlY");
        print("object:drawendx:$endX---endY:$endY");

        //按等比例缩放移动x，y点
        endX= p03.dx - (p03.dx - p00.dx) *radius / twoPointDistance;
        endY = p03.dy - ( p03.dy - p00.dy) * radius / twoPointDistance;

        double lenX = (endX - p00.dx);
        double lenY = (endY - p00.dy);
        double lenXY =  sqrt((lenX * lenX + lenY * lenY));
        // print("object:radian lenXY 角度:$lenXY---");
        // print("object:radian lenXY 角度--:${twoPointDistance-radius}---");
        print("object:radian lenXY 角度--:${acos(lenX / lenXY)}---");
        print("object:radian lenXY 角度--:${acos(linkLineX / (twoPointDistance-radius))}---");
        print("object:radian lenXY 角度--:${acos(sinAngle)}---");

        //得出弧度，lenX / lenXY 为出发点圆夹角
        double radian =acos(lenX / lenXY) * (p00.dy <  p03.dy ? 1 : -1);

        //得出，半圆为Π，得出补角弧度，pi / 2-radian
        double centerRadian = pi / 2-radian;
        double  tempAssX = (centerX + lenXY / 4 *cos(centerRadian));
        double tempAssY = (centerY - lenXY / 4 *sin(centerRadian));


        //计算两边的箭头点，比例是试出来的
        double  arrowAssX = (centerX + lenXY  *cos(centerRadian));
        double  arrowAssY = (centerY - lenXY  *sin(centerRadian));
        double  arrowAssX2 = (centerX - lenXY/3   *cos(centerRadian));
        double  arrowAssY2 = (centerY + lenXY /3 *sin(centerRadian));


        var path = Path();
        path.moveTo(p00.dx, p00.dy);
        // path.quadraticBezierTo(controlX , controlY, p03.dx, p03.dy);
        // path.quadraticBezierTo(controlX , controlY, endX,endY);
        path.quadraticBezierTo(tempAssX , tempAssY, endX,endY);
        // path.lineTo(endX,endY);
        canvas.drawPath(path, _painterLine);




        //画箭头,
        drawArrow(canvas,centerX,centerY, arrowAssX,arrowAssY,endX,endY,arrowAssX2,arrowAssY2);


        //中心点
        Paint _painterLine1 = Paint()
          ..color = Color(0xFFFFFFFF)
          ..style = PaintingStyle.fill
          ..isAntiAlias = true;
        canvas.drawCircle(
            Offset(centerX, centerY), 2, _painterLine1);

        //控制心点
        Paint _painterLine2 = Paint()
          ..color = Color(0xFFFFA100)
          ..style = PaintingStyle.fill
          ..isAntiAlias = true;
        canvas.drawCircle(
            Offset(tempAssX, tempAssY), 2, _painterLine2);

      }
    }
  }

  @override
  bool shouldRepaint(covariant CustomPainter oldDelegate) {
    return true;
  }


  void drawArrow(Canvas canvas,double centerX,double centerY,double controlX,double controlY,double endX,double endY,double controlX2,double controlY2,){
    Paint _painterArrow = Paint()
      ..color = Color(0xFFF50000)
      ..style = PaintingStyle.stroke
      ..isAntiAlias = true;
    var pathArrow = Path();

    double controlEndDistance = sqrt((endX-controlX) * (endX-controlX) + (endY-controlY) * (endY-controlY));

    //进行比例缩放得出箭头点
    double  lineToX= endX - (endX - controlX) *10 / controlEndDistance;
    double  lineToY= endY - (endY - controlY) *10 / controlEndDistance;

    double  lineToX2= endX - (endX - controlX2) *18 / controlEndDistance;
    double  lineToY2= endY - (endY - controlY2) *18 / controlEndDistance;

    pathArrow.moveTo(endX,endY);
    pathArrow.lineTo(lineToX,lineToY);
    pathArrow.moveTo(endX,endY);
    pathArrow.lineTo(lineToX2,lineToY2);
    canvas.drawPath(pathArrow, _painterArrow);
  }
}

class PortPath extends AbstractPath {
  final Offset position;
  final Offset positionHead;
  final Size size;

  PortPath(this.positionHead, this.position, this.size);

  @override
  Path formPath() {
    Path path = Path();
    Rect zone = Rect.fromCenter(
        center: position, width: size.width, height: size.height);
    Offset p0 = zone.centerLeft;
    Offset p1 = zone.bottomRight;
    Offset p2 = zone.topRight;
    path
      ..moveTo(p0.dx, p0.dy)
      ..lineTo(p1.dx, p1.dy)
      ..lineTo(p2.dx, p2.dy)
      ..close();

    double controlX = 0;
    double controlY = 0;
    Offset line = position - positionHead;
    Offset center = positionHead + line / 2;
    double length = line.distance;
    Path headPath = path;
    double fixDx = size.width / 2 * cos(line.direction);
    double fixDy = size.height / 2 * sin(line.direction);
    Matrix4 headM4 = Matrix4.translationValues(fixDx, fixDy, 0);

    headM4.multiply(Matrix4.translationValues(center.dx, center.dy, 0));
    headM4.multiply(Matrix4.rotationZ(line.direction));
    headM4.multiply(Matrix4.translationValues(-center.dx, -center.dy, 0));
    headPath = headPath.transform(headM4.storage);
    return path;

    // Matrix4 headM4 = Matrix4.translationValues(fixDx, fixDy, 0);
    // headPath = headPath.transform(headM4.storage);
    // Path tailPath = position.formPath();
    // Matrix4 tailM4 = Matrix4.translationValues(-fixDx, -fixDy, 0);
    // tailPath = tailPath.transform(tailM4.storage);

    // if(positionHead.dx>position.dx){
    //   controlX =position.dx+ (position.dx-positionHead.dx).abs()/2;
    //   if(positionHead.dy>position.dy){
    //     controlY =position.dy+ (position.dy-positionHead.dy).abs()/2;
    //     line = Offset(0, 0);
    //   }else{
    //     controlY =positionHead.dy+ (position.dy-positionHead.dy).abs()/2;
    //   }
    // }else{
    //   controlX =positionHead.dx+ (position.dx-positionHead.dx).abs()/2;
    //   if(positionHead.dy>position.dy){
    //     controlY =position.dy+ (position.dy-positionHead.dy).abs()/2;
    //   }else{
    //     controlY =positionHead.dy+ (position.dy-positionHead.dy).abs()/2;
    //   }
    // }   Offset line = Offset(dx, dy);
    //
    // Path headPath = head.formPath();
    // double fixDx = head.size.width/2*cos(line.direction);
    // double fixDy = head.size.height/2*sin(line.direction);
    //
    //
    //
    // Matrix4 headM4 = Matrix4.translationValues(fixDx, fixDy, 0);
    //
    // headM4.multiply(Matrix4.translationValues(controlX, controlY, 0));
    // headM4.multiply(Matrix4.rotationZ(line.direction));
    // headM4.multiply(Matrix4.translationValues(-controlX, -controlY, 0));
    // path = path.transform(headM4.storage);

    return path;
  }
}

class ArrowPath extends AbstractPath {
  final PortPath head;
  final PortPath tail;

  ArrowPath({required this.head, required this.tail});

  @override
  Path formPath() {
    Offset line = (tail.position - head.position);
    Offset center = head.position + line / 2;
    double length = line.distance;
    Rect lineZone = Rect.fromCenter(center: center, width: length, height: 2);
    Path linePath = Path()..addRect(lineZone);

    // 通过矩阵变换，让 linePath 以 center 为中心旋转 两点间角度
    Matrix4 lineM4 = Matrix4.translationValues(center.dx, center.dy, 0); // tag1
    lineM4.multiply(Matrix4.rotationZ(line.direction));
    lineM4
        .multiply(Matrix4.translationValues(-center.dx, -center.dy, 0)); // tag2
    linePath = linePath.transform(lineM4.storage);

    Path temp = Path.combine(PathOperation.union, linePath, head.formPath());
    return Path.combine(PathOperation.union, temp, tail.formPath());
  }
}

abstract class AbstractPath {
  Path formPath();

// void ra(){
//   Matrix4 headM4 = Matrix4.translationValues(fixDx, fixDy, 0);
//   center = head.position;
//   headM4.multiply(Matrix4.translationValues(center.dx, center.dy, 0));
//   headM4.multiply(Matrix4.rotationZ(line.direction));
//   headM4.multiply(Matrix4.translationValues(-center.dx, -center.dy, 0));
//   headPath = headPath.transform(headM4.storage);
//
//   Matrix4 tailM4 = Matrix4.translationValues(-fixDx, -fixDy, 0);
//   center = tail.position;
//   tailM4.multiply(Matrix4.translationValues(center.dx, center.dy, 0));
//   tailM4.multiply(Matrix4.rotationZ(line.direction-pi)); // tag3
//   tailM4.multiply(Matrix4.translationValues(-center.dx, -center.dy, 0));
//   tailPath = tailPath.transform(tailM4.storage);
// }
}

class BeiSaierUtils {
  static Offset get3OrderBezierPoint(
      Offset p1, Offset p2, Offset p3, Offset p4, double t) {
    var x = (1 - t) * (1 - t) * (1 - t) * p1.dx +
        3 * t * (1 - t) * (1 - t) * p2.dx +
        3 * t * t * (1 - t) * p3.dx +
        t * t * t * p4.dx;
    var y = (1 - t) * (1 - t) * (1 - t) * p1.dy +
        3 * t * (1 - t) * (1 - t) * p2.dy +
        3 * t * t * (1 - t) * p3.dy +
        t * t * t * p4.dy;

    return Offset(x, y);
  }

  static drawBezierCurve(
      Canvas canvas, Paint paint, List<Offset> points, Size size) {
    assert(points.length == 4);
    var path = Path();
    path.moveTo(points[0].dx, points[0].dy);
    for (var t = 1; t <= 100; t += 1) {
      var curvePoint = get3OrderBezierPoint(
          points[0], points[1], points[2], points[3], t / 100.0);

      path.lineTo(curvePoint.dx, curvePoint.dy);
    }
    canvas.drawPath(path, paint);
  }
}
